#include "graphics.h"

SDL_Texture *background_texture;

void init_graphics() {
    background_texture = NULL;
    render_changed = 0;

    window = SDL_CreateWindow(
        WINDOW_TITLE,
        /* initial pos of window */
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        /* window scale */
        WINDOW_W, WINDOW_H,
        /* show immediately, highdpi, resizability */
        SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI
        //SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE
        /* TODO: fix window resizing */
    );

    if (window == NULL) {
        fprintf(stderr, "SDL_CreateWindow error: %s\n", SDL_GetError());
        exit(1);
    }

    /* creates renderer
       -1 specifies the loading of whichever video driver supports our flags
       SDL_RENDERER_ACCELERATED:  use hardware accelerating
       SDL_RENDERER_PRESENTVSYNC: synchronize present function (update display) with monitor refresh rate
    */
    render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);

    if (render == NULL) {
        fprintf(stderr, "SDL_CreateRenderer error: %s\n", SDL_GetError());
        exit(-1);
    }

    SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND);

    /* texture for render context */
    display = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, WINDOW_W, WINDOW_H);

    /* font to use */
    font = NULL;
    /* try different font locations */
    if (font == NULL)
        font = TTF_OpenFont("/usr/share/fonts/TTF/mplus-1m-regular.ttf", FONTSIZE);
    if (font == NULL)
        font = TTF_OpenFont("/usr/share/fonts/TTF/Inconsolata-Regular.ttf", FONTSIZE);
    if (font == NULL)
        font = TTF_OpenFont("./src/font/mplus-1m-regular.ttf", FONTSIZE);
    if (font == NULL) {
        font = TTF_OpenFont("./src/font/Inconsolata-Regular.ttf", FONTSIZE);
    if (font == NULL)
        fprintf(stderr,
                "\nTTF_OpenFont Error:  %s\n",
                SDL_GetError());
        exit(1);
    }

    TTF_SetFontHinting(font, TTF_HINTING_MONO);

    SDL_SetRenderTarget(render, display);
}

void set_render_changed() {
    render_changed = 1;
}

void pre_render() {
    SDL_SetRenderTarget(render, display);
}

void update_render() {
    if (render_changed) {
        SDL_SetRenderTarget(render, NULL);
        SDL_RenderCopy(render, display, NULL, NULL);

        SDL_RenderPresent(render);
        render_changed = 0;
    }
}

void cleanup_graphics() {
    SDL_DestroyRenderer(render);
    SDL_DestroyWindow(window);
}

void draw_bg(uint32_t color) {
    SDL_RenderClear(render);

    if (background_texture == NULL) {
        SDL_SetRenderDrawColor(render, partr(color), partg(color), partb(color), parta(color));
        draw_rect(0, 0, WINDOW_W, WINDOW_H);
    } else {
        int w = 0, h = 0, offset_w = 0, offset_h = 0;
        SDL_QueryTexture(background_texture, NULL, NULL, &w, &h);
        /* if we need to scale down */
        if (w > WINDOW_W || h > WINDOW_H) {
            float ratio_w = (float) w / (float) WINDOW_W;
            float ratio_h = (float) h / (float) WINDOW_H;
            /* we decide to follow the less extreme conversion, that */
            /* is to say the lesser ratio */
            if (ratio_w > ratio_h) {
                w /= ratio_h;
                h /= ratio_h;
            } else {
                w /= ratio_w;
                h /= ratio_w;
            }
        /* if we need to scale up */
        } else if (w < WINDOW_W && h < WINDOW_H) {
            float ratio_w = (float) WINDOW_W / (float) w;
            float ratio_h = (float) WINDOW_H / (float) h;
            if (ratio_h > ratio_w) {
                w *= ratio_h;
                h *= ratio_h;
            } else {
                w *= ratio_w;
                h *= ratio_w;
            }
        }

        /* if a side is still larget than the max, we need to offset the */
        /* image to effectively center it */
        if (w > WINDOW_W)
            offset_w = -((w - WINDOW_W) / 2);
        if (h > WINDOW_H)
            offset_h = -((h - WINDOW_H) / 2);

        /* render image */
        SDL_Rect render_quad = { offset_w, offset_h, w, h };
        SDL_RenderCopy(render, background_texture, NULL, &render_quad);
    }
}

void draw_rect(int x, int y, int w, int h) {
    SDL_Rect bg = { x, y, w, h };
    SDL_RenderDrawRect(render, &bg);
    set_render_changed();
}

static void draw_cover_art_wrapped(unsigned char *image, int image_size) {
    if (image_size == 0 || image == NULL)
        return;

    if (DEBUG)
        printf("commence drawing of image\n");

    SDL_Surface *result = NULL;
    SDL_RWops *stream = SDL_RWFromConstMem(image, image_size);
    if (stream == NULL) {
        // error handling for failure - call SDL_GetError()
        fprintf(stderr, "stream NULL: %s\n", SDL_GetError());
        return;
    } else {
        if (IMG_isPNG(stream))
            result = IMG_LoadPNG_RW(stream);
        else if (IMG_isJPG(stream))
            result = IMG_LoadJPG_RW(stream);
        else {
            /* special case needs pre-emptive exit */
            printf("neither a png nor jpg!\n");
            if (background_texture != NULL) {
                SDL_DestroyTexture(background_texture);
                background_texture = NULL;
            }
            SDL_RWclose(stream);
            if (image != NULL)
                free(image);
            set_render_changed();
            return;
        }
        SDL_RWclose(stream);
    }

    if (result == NULL) {
        fprintf(stderr, "surface NULL\n");
        return;
    }

    SDL_Texture* image_texture = SDL_CreateTextureFromSurface(render, result);

    if (image_texture == NULL) {
        fprintf(stderr,
                "\nSDL_CreateTextureFromSurface Error:  %s\n",
                SDL_GetError());
        exit(1);
    }

    if (background_texture != NULL)
        SDL_DestroyTexture(background_texture);
    background_texture = image_texture;

    free(image);
    SDL_FreeSurface(result);

    set_render_changed();
}

void draw_frames(int frame) {
    /* create framerate string to draw */
    /* string length according to digits */
    char str[frame / 10 - (frame % 10) + 1];
    sprintf(str, "frame %d", frame);
    draw_text_and_bg(-BORDER_W, BORDER_H + FONTHEIGHT, str, 0xffffff88);
}

void draw_cover_art() {
    unsigned char *image = NULL;
    int image_size = 0;
    /* read id3 */
    read_image(cr_track->file, &image, &image_size);
    /* proceed to draw */
    if (image_size > 0 && image != NULL)
        draw_cover_art_wrapped(image, image_size);
}

void draw_info() {
    /* create framerate string to draw */
    /* string length according to digits */
    if (cr_track->title_tag != NULL) {
        //clip_string_dotted(cr_track->title_tag, max_l_w);
        draw_text_and_bg(BORDER_W, -BORDER_H, cr_track->title_tag, 0x11111166);
    }
    if (cr_track->artist_tag != NULL) {
        //clip_string_dotted(cr_track->artist_tag, max_l_w);
        draw_text_and_bg(BORDER_W, -BORDER_H - FONTHEIGHT, cr_track->artist_tag, 0x11111166);
    }
    if (cr_track->album_tag != NULL) {
        draw_text_and_bg(BORDER_W, -BORDER_H - 3 * FONTHEIGHT, cr_track->album_tag, 0x11111166);
    }
    //draw_text_and_bg(BORDER_W, BORDER_H , cr_player->playing ? "status: playing" : "status: paused", 0x11111166);
    draw_text_and_bg(BORDER_W, BORDER_H , cr_player->status, 0x11111166);

    if (cr_track->duration > 0 && cr_player->position < cr_track->duration) {
        /* doesn't support hour long songs lol */
        char time_str[3 + 2 * 5];
        sprintf(
            time_str,
            "%.2d:%.2d / %.2d:%.2d",
            /* minutes, keep it 2 digits */
            ((cr_player->position / 60) % 100),
            (cr_player->position % 60),
            /* minutes, keep it 2 digits */
            ((cr_track->duration / 60) % 100),
            (cr_track->duration % 6)
        );
        draw_text_and_bg(-BORDER_W, BORDER_H, time_str, 0xffffff55);
    }
}

static void draw_text_with_bg(int x, int y, char *text, uint32_t color, bool with_bg, bool clip) {
    SDL_Color text_rgb = { partr(color), partg(color), partb(color) };

    /* ugliest code so far lol */
    int w, h, available_w = WINDOW_W - BORDER_W;
    bool duped = false;
    TTF_SizeUTF8(font, text, &w, &h);
    if (clip && w > available_w) {
        int text_len = strlen(text);
        int avg_width = w / text_len;
        int cut_count = 0;
        duped = true;
        text = strdup(text);

        while (w > available_w) {
            w -= avg_width;
            ++cut_count;
        }

        if (text_len - 4 - cut_count > 0) {
            text[text_len - 4 - cut_count] = '.';
            text[text_len - 3 - cut_count] = '.';
        }
        if (text_len - 2 - cut_count > 0)
            text[text_len - 2 - cut_count] = '\0';
    }

    SDL_Surface* text_surface = TTF_RenderUTF8_Blended(font, text, text_rgb);

    /* if previously duped, happens during clipping */
    if (duped)
        free(text);
    
    if (text_surface == NULL) {
        fprintf(stderr,
                "\nTTF_RenderText_Solid Error:  %s\n",
                SDL_GetError());
        exit(1);
    }

    SDL_Texture* message_surface = SDL_CreateTextureFromSurface(render, text_surface);

    if (message_surface == NULL) {
        fprintf(stderr,
                "\nSDL_CreateTextureFromSurface Error:  %s\n",
                SDL_GetError());
        exit(1);
    }

    int message_w = text_surface->w;
    int message_h = text_surface->h;

    if (with_bg) {
        /* background time */
        uint32_t anti_color = (0xffffffff - color);
        SDL_SetRenderDrawColor(render, partr(anti_color), partg(anti_color), partb(anti_color), parta(anti_color));
        SDL_Rect render_quad1 = { posx(x, message_w), posy(y, message_h), message_w, message_h };
        SDL_RenderFillRect(render, &render_quad1);
    }

    /* text time */
    SDL_Rect render_quad2 = { posx(x, message_w), posy(y, message_h), message_w, message_h };
    SDL_RenderCopy(render, message_surface, NULL, &render_quad2);

    SDL_DestroyTexture(message_surface);
    SDL_FreeSurface(text_surface);

    set_render_changed();;
}

void draw_text_and_bg(int x, int y, char *text, uint32_t color) {
    draw_text_with_bg(x, y, text, color, true, true);
}

void draw_text(int x, int y, char *text, uint32_t color) {
    draw_text_with_bg(x, y, text, color, false, true);
}

void draw_img(char *file, SDL_Rect* dstrect) {
    display = IMG_LoadTexture(render, file);
    if (display == NULL) {
        fprintf(stderr, "draw_img error: %s\n", SDL_GetError());
        exit(1);
    }
    if (SDL_SetTextureBlendMode(display, SDL_BLENDMODE_BLEND)) {
        fprintf(stderr, "draw_img error: %s\n", SDL_GetError());
        exit(1);
    }

    SDL_Rect srcrect = { 0, 0, WINDOW_W, WINDOW_H };
    SDL_RenderCopy(render, display, &srcrect, dstrect);
    set_render_changed();
}

/* extract rgba part from uint32_t */
int partr(uint32_t color) {
    return (color & 0xFF000000) >> 24;
}

int partg(uint32_t color) {
    return (color & 0x00FF0000) >> 16;
}

int partb(uint32_t color) {
    return (color & 0x0000FF00) >> 8;
}

int parta(uint32_t color) {
    return (color & 0x000000FF) >> 0;
}

/* calculate position of texture to be placed */
/* negative numbers automatically offset from the opposite side */
int posx(int x, int w) {
    if (x < 0)
        return WINDOW_W - w + x;
    else
        return x;
}

int posy(int y, int h) {
    if (y < 0)
        return WINDOW_H - h + y;
    else
        return y;
}
